# coding: utf-8
import os
import re
import subprocess
import time
import thread


class Singleton(object):
    objs = {}

    def __new__(cls, *args, **kv):
        if cls in cls.objs:
            return cls.objs[cls]
        cls.objs[cls] = object.__new__(cls)
        return cls.objs[cls]


class AdbManager(Singleton):
    def __init__(self, package_name="com.pp.assistant"):
    # def __init__(self, package_name="com.taobao.appcenter"):
        if not hasattr(self, 'package_name'):
            self.package_name = package_name
            # self.index_selected = 0
            self.sn_selected = ""
            self.devices_sn = []
            self.devices = {}
            self.cmd_queue = []
            self.is_batch = False

            thread.start_new_thread(self.loop_cmd_queue, ())

    def receive_action(self, action, is_batch, **kwargs):
        if self.devices_sn[0] == "no connect!":  # 处理无设备连接情况
            print "no connect!"
            return
        sn_current = self.sn_selected
        try:
            if is_batch:
                sn_list = self.devices_sn
            else:
                sn_list = [sn_current]
            for sn in sn_list:
                self.sn_selected = sn  # 不想每个adb的方法都使用传参的方式，就直接使用类变量来传递真是的adb_sn吧

                self.execute_action(action, **kwargs)
        finally:
            self.sn_selected = sn_current

    def execute_action(self, action, **kwargs):
        method = getattr(self, action)
        try:
            method(**kwargs)
        except Exception as e:
            print e

    def cmd(self, command, need_result=False):
        if need_result:
            print "CMD command:", command
            return os.popen(command)
        elif not self.is_batch:
            print "CMD command:", command
            return os.system(command)
        else:
            self.cmd_queue.append(command)
            # os.system(command)
            return ""

    def loop_cmd_queue(self):
        while True:
            if len(self.cmd_queue):
                command = self.cmd_queue.pop()
                print "CMD command:", command
                # os.popen(command)
                obj = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
                time.sleep(0.5)
                obj.kill()
            else:
                time.sleep(0.3)

    def update_sn_by_index(self, index_selected):
        self.sn_selected = self.devices_sn[index_selected]

    def get_devices(self):
        devices_sn = self.adb_devices()
        print len(devices_sn), "connected devices:", ", ".join(devices_sn)
        # 根据已有的设备进行增删
        devices_sn_old = self.devices.keys()
        devices_sn_remove = [sn for sn in devices_sn_old if sn not in devices_sn]
        if len(devices_sn_remove):
            print "remove", len(devices_sn_remove), "device(s)"
        for sn in devices_sn_remove:
            self.devices.pop(sn)
        devices_sn_add = [sn for sn in devices_sn if sn not in devices_sn_old]
        if len(devices_sn_add):
            print "add", len(devices_sn_add), "device(s)"
        for sn in devices_sn_add:
            device_info = self.adb_device_info(sn)
            # device_info.append(device_sn)
            self.devices[sn] = " ".join(device_info)  # 以android studio的设备显示格式来显示设备
        if len(self.devices) < 1:
            self.devices["no connect!"] = "no device!"  # 处理空值
        self.devices_sn = self.devices.keys()
        return self.devices.values()

    # def common(self):
    #     if self.device in ("", "no connect!"):
    #         self.device_sn = ""
    #     elif self.device.find("DISCONNECTED"):
    #         self.device_sn = None
    #     else:
    #         self.device_sn = "-s %s" % self.device_sn

    def adb_force_stop(self, package_name=""):
        if package_name == "":
            package_name = self.package_name
        command = 'adb -s %s shell am force-stop %s' % (self.sn_selected, package_name)
        self.cmd(command)

    def adb_clean_data(self, package_name=""):
        if package_name == "":
            package_name = self.package_name
        command = 'adb -s %s shell pm clear %s' % (self.sn_selected, package_name)
        self.cmd(command)

    def adb_uninstall(self, package_name=""):
        if package_name == "":
            package_name = self.package_name
        command = 'adb -s %s uninstall %s' % (self.sn_selected, package_name)
        self.cmd(command)

    def adb_install(self, file_path):
        command = 'adb -s %s install -r %s' % (self.sn_selected, file_path)
        self.cmd(command)

    def adb_open_apk(self, package, activity, launcher="android.intent.category.LAUNCHER"):
        command = ("adb -s %s shell am start " % self.sn_selected +
                   "-n %s/%s " % (package, activity) +
                   "-c %s" % launcher)
        self.cmd(command)

    # def adb_open_pp(self):
    #     command = ("adb -s %s shell am start " % self.sn_selected +
    #                "-n com.pp.assistant/com.pp.assistant.activity.PPMainActivity " +
    #                "-c android.intent.category.LAUNCHER")
    #     self.cmd(command)

    # def adb_open_apk(self, package, activity):
    #     command = ("adb -s %s shell am start " % self.sn_selected +
    #                "-n %s/%s" % (package, activity))
    #     self.cmd(command)

    def adb_uninstall_all(self):
        """批量卸载设备上的应用"""
        white_list = ["com.speedsoftware.rootexplorer"]
        print "start uninstall..."

        # adb shell pm list packages -3: filter to only show third party packages
        package_3p = os.popen("adb -s %s shell pm list packages -3 " % self.sn_selected)

        for packages in package_3p.readlines():
            package_name = packages.split(":")[-1].splitlines()[0]
            if (package_name in white_list or
                    package_name.startswith('com.pp') or
                    package_name.startswith('com.uc') or
                    package_name.startswith('com.wandoujia')):
                print package_name + " in whiteList"
            else:
                os.popen("adb -s %s uninstall %s" % (self.sn_selected, package_name))
                print "remove %s successes." % package_name

    def adb_back_key(self):
        """发送返回键"""
        command = "adb -s %s shell input keyevent 4" % self.sn_selected
        self.cmd(command)

    def adb_screencap(self, remote_path="/sdcard/screen.png"):
        command = "adb -s %s shell screencap -p %s" % (self.sn_selected, remote_path)
        self.cmd(command)

    def adb_pull_file(self, remote_path, local_path):
        """发送返回键"""
        command = "adb -s %s pull %s %s" % (self.sn_selected, remote_path, local_path)
        self.cmd(command)

    def adb_push_file(self, remote_path, local_path):
        """发送返回键"""
        command = "adb -s %s push %s %s" % (self.sn_selected, local_path, remote_path)
        self.cmd(command)

    def adb_remove_file(self, remote_path, is_rf=False):
        """清理文件"""
        if len(remote_path) < len("/sdcard/pp"):
            print "adb_remove_file warning: remote path is too short!"
            return
        if not remote_path.startswith("/sdcard/"):
            print "adb_remove_file warning: can only remove file/folder from sdcard!"
            return
        if is_rf:
            rf = "-rf "
        else:
            rf = ""
        command = "adb -s %s shell rm %s%s" % (self.sn_selected, rf, remote_path)
        self.cmd(command)

    def adb_devices(self):
        command = 'adb devices'
        devices_sn = []
        adb_out = self.cmd(command, need_result=True).readlines()
        for line in adb_out:
            m = re.match(r"(\S+)\tdevice", line)
            if m:
                devices_sn.append(m.group(1))
        return devices_sn

    @staticmethod
    def adb_device_info(device_sn):
        # print "get device info"
        brand = os.popen("adb -s %s shell getprop ro.product.brand" % device_sn).read()
        model = os.popen("adb -s %s shell getprop ro.product.model" % device_sn).read()
        version = "Android " + os.popen("adb -s %s shell getprop ro.build.version.release" % device_sn).read()
        api_level = ",API " + os.popen("adb -s %s shell getprop ro.build.version.sdk" % device_sn).read()
        return [brand, model, version, api_level]

    def adb_monkey_test(self, paras):
        seed, throttle, count, package = paras
        command = ("adb -s %s shell monkey -s  %s " % (self.sn_selected, seed) +
                   # "-p com.pp.assistant -v -v --throttle %s " % throttle +
                   "-p %s " % package +
                   "-v -v --throttle %s " % throttle +
                   "--ignore-crashes --ignore-security-exceptions --ignore-timeouts %s" % count)
        # print command
        obj = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        time.sleep(0.5)
        obj.kill()


# if __name__ == "__main__":
#     AdbCommand.adb_devices()
